﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.MediaCenter.UI;
using Microsoft.MediaCenter;
using Microsoft.MediaCenter.Hosting;
using System.Diagnostics;
using System.Threading;
using System.IO;
using System.Windows.Forms;
using Microsoft.Win32;
using System.Configuration;
using System.Reflection;

namespace BoxeeLauncher
{
    public class Application : ModelItem
    {
        public Application(AddInHost host)
        {
            _host = host;

            if(string.IsNullOrEmpty(_logPath))
            {
                // Create a log file
                DirectoryInfo pathInfo = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + Path.DirectorySeparatorChar + "BoxeeLauncher");
                if (!pathInfo.Exists)
                {
                    pathInfo.Create();
                }

                FileInfo logFile = new FileInfo(pathInfo.FullName + Path.DirectorySeparatorChar + "logfile.txt");
                if (!logFile.Exists)
                {
                    logFile.Create();
                }

                _logPath = logFile.FullName;
            }

            try
            {
                // Load the dll.config so we can get our settings.
                string configPath = PluginDirectory.FullName + Path.DirectorySeparatorChar + "BoxeeLauncher.dll.config";
                if (File.Exists(configPath))
                {
                    ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap()
                    {
                        ExeConfigFilename = configPath
                    };

                    Configuration config = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);

                    _isBackgroundWindowEnabled = bool.Parse(config.AppSettings.Settings["IsBackgroundWindowEnabled"].Value);
                    _backgroundWindowColor = System.Drawing.Color.FromName(config.AppSettings.Settings["BackgroundWindowColor"].Value);
                }
                else
                {
                    throw new InvalidOperationException("Could not find BoxeeLauncher.dll.config.");
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                throw;
            }
        }

        private const byte VK_ALT = 0x12;
        private const int KEYEVENT_KEYUP = 0x02;
        private const int WM_SYSCOMMAND = 274;
        private const int SC_MAXIMIZE = 61488;
        private const int SC_MINIMIZE = 61472;
        private const int SWP_SHOWWINDOW = 64;

        private const int SW_HIDE = 0;
        private const int SW_SHOW = 1;

        private bool _isBackgroundWindowEnabled = true;
        private System.Drawing.Color _backgroundWindowColor = System.Drawing.Color.Black;
        private static IntPtr HWND_TOP = IntPtr.Zero;
        private AddInHost _host = null;
        private static string _logPath = string.Empty;
        private static Process _mediaCenterProcess = null;

        private DirectoryInfo _pluginDirectory = null;
        public DirectoryInfo PluginDirectory
        {
            get
            {
                if (_pluginDirectory == null)
                {
                    // Find the Boxee install path from the registry
                    string installPath = string.Empty;
                    using (RegistryKey hkcuKey = Registry.LocalMachine)
                    {
                        using (RegistryKey pluginKey = hkcuKey.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Media Center\Extensibility\Entry Points\{835F9D34-8B2B-41E7-90A0-0878AD03C946}", false))
                        {
                            if (pluginKey == null)
                            {
                                throw new InvalidOperationException(@"Could not find plugin registration path at HKLM\Software\Microsoft\Windows\CurrentVersion\Media Center\Extensibility\Entry Points\{835F9D34-8B2B-41E7-90A0-0878AD03C946}.");
                            }

                            // Get the default value
                            object imageValue = pluginKey.GetValue("ImageUrl");
                            if (imageValue == null)
                            {
                                throw new InvalidOperationException(@"Could not find ImageUrl key for plugin registration.");
                            }

                            _pluginDirectory = new FileInfo(imageValue.ToString()).Directory;
                        }
                    }
                }

                return _pluginDirectory;
            }
        }

        private static System.Threading.Timer _windowWatchTimer = new System.Threading.Timer((sender)=>
            {
                try
                {
                    // Get the media center process (we're currently in exexthost or something)
                    if (_mediaCenterProcess == null)
                    {
                        Process[] foundMCProcess = Process.GetProcessesByName("ehshell");
                        if (foundMCProcess.Length > 0)
                        {
                            _mediaCenterProcess = foundMCProcess[0];
                        }
                    }

                    if (_mediaCenterProcess == null)
                    {
                        _windowWatchTimer.Change(Timeout.Infinite, Timeout.Infinite);
                        return;
                    }

                    if (_mediaCenterProcess.MainWindowHandle == GetForegroundWindow())
                    {
                        Process boxeeHostProcess = null;
                        Process[] foundProcess = Process.GetProcessesByName("BOXEE");
                        if (foundProcess.Length > 0)
                        {
                            boxeeHostProcess = foundProcess[0];
                            boxeeHostProcess.CloseMainWindow();
                            _windowWatchTimer.Change(Timeout.Infinite, Timeout.Infinite);
                        }
                    }
                }
                catch (Exception ex)
                {
                    _windowWatchTimer.Change(Timeout.Infinite, Timeout.Infinite);
                    LogException(ex);
                    throw ex;
                }

            }, null, Timeout.Infinite, 500);


        public void GoToMenu()
        {
            try
            {

                Form backgroundForm = null;
                try
                {
                    Dictionary<string, object> properties = new Dictionary<string, object>();
                    properties["Application"] = this;

                    System.Drawing.Rectangle screenBounds = Screen.PrimaryScreen.Bounds;

                    // Not going to work right if MC is in a secondary screen 
                    ShowCursor(false);
                    SetCursorPos(screenBounds.Width + 5000, 0);

                    if (_mediaCenterProcess == null)
                    {
                        // Get the media center process (we're currently in exexthost or something)
                        Process[] foundMCProcess = Process.GetProcessesByName("ehshell");
                        if (foundMCProcess.Length > 0)
                        {
                            _mediaCenterProcess = foundMCProcess[0];
                        }
                    }

                    // Find the Boxee install path from the registry
                    string boxeePath = string.Empty;
                    RegistryKey hkcuKey = null;
                    RegistryKey hklmKey = null;
                    RegistryKey boxeeKey = null;
                    try
                    {
                        hklmKey = Registry.LocalMachine;
                        boxeeKey = hklmKey.OpenSubKey(@"Software\BOXEE");
                        if (boxeeKey == null)
                        {
                            boxeeKey = hklmKey.OpenSubKey(@"Software\Wow6432Node\BOXEE");
                            if (boxeeKey == null)
                            {
                                hkcuKey = Registry.CurrentUser;
                                boxeeKey = hkcuKey.OpenSubKey(@"Software\BOXEE");
                                if (boxeeKey == null)
                                {
                                    throw new InvalidOperationException(@"BOXEE is not installed properly. Registry key could not be found at HKCU\Software\BOXEE, HKLM\Software\BOXEE, and HKLM\Software\Wow6432Node\BOXEE.");
                                }
                            }
                        }

                        // Get the default value
                        object pathValue = boxeeKey.GetValue(null);
                        if (pathValue == null)
                        {
                            throw new InvalidOperationException(@"BOXEE is not installed properly. Registry key default value could not be found at HKCU\Software\BOXEE.");
                        }

                        DirectoryInfo dirInfo = new DirectoryInfo(pathValue.ToString());
                        if (!dirInfo.Exists)
                        {
                            throw new InvalidOperationException(@"BOXEE is not installed properly. Registered install path could not be found.");
                        }

                        FileInfo fileInfo = new FileInfo(dirInfo.FullName + Path.DirectorySeparatorChar + "BOXEE.exe");
                        if (!fileInfo.Exists)
                        {
                            throw new InvalidOperationException(@"BOXEE is not installed properly. BOXEE.exe could not be found.");
                        }

                        boxeePath = fileInfo.FullName;
                    }
                    finally
                    {
                        if (boxeeKey != null)
                        {
                            boxeeKey.Close();
                        }

                        if (hkcuKey != null)
                        {
                            hkcuKey.Close();
                        }

                        if (hklmKey != null)
                        {
                            hklmKey.Close();
                        }
                    }

                    // Let's stop playback before we launch boxee
                    if (_host.MediaCenterEnvironment.MediaExperience != null &&
                        _host.MediaCenterEnvironment.MediaExperience.Transport != null)
                    {
                        _host.MediaCenterEnvironment.MediaExperience.Transport.PlayRate = PlayRate.Stop;
                    }

                    Process boxeeLaunchProcess = Process.Start(boxeePath);

                    if (_isBackgroundWindowEnabled)
                    {
                        // Before we move the windows around, create a black background window
                        // that covers the taskbar so that the user doesn't see the desktop
                        backgroundForm = new Form()
                        {
                            AllowTransparency = true,
                            BackColor = _backgroundWindowColor,
                            ControlBox = false,
                            FormBorderStyle = FormBorderStyle.None,
                            HelpButton = false,
                            MaximizeBox = false,
                            MinimizeBox = false,
                            ShowIcon = false,
                            ShowInTaskbar = false,
                            SizeGripStyle = SizeGripStyle.Hide,
                        };

                        string backgroundPath = PluginDirectory.FullName + Path.DirectorySeparatorChar + "Background.png";
                        if (File.Exists(backgroundPath))
                        {
                            backgroundForm.BackgroundImage = new System.Drawing.Bitmap(backgroundPath);
                            backgroundForm.BackgroundImageLayout = ImageLayout.Center;
                        }

                        backgroundForm.Show();
                        SetWindowPos(backgroundForm.Handle, HWND_TOP, 0, 0, screenBounds.Width, screenBounds.Height, SWP_SHOWWINDOW);
                    }
                    else
                    {
                        // Wait a bit so we show the desktop for the least amount of time possible
                        Thread.Sleep(300);
                    }

                    HideTaskbar();

                    if (_mediaCenterProcess != null)
                    {
                        SendMessage(_mediaCenterProcess.MainWindowHandle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
                        // Yeah, I do this all over the place, but that taskbar really wants to get in front everytime window
                        // state changes, so do this after we maximize / minimize windows
                        if (backgroundForm != null)
                        {
                            SetWindowPos(backgroundForm.Handle, HWND_TOP, 0, 0, screenBounds.Width, screenBounds.Height, SWP_SHOWWINDOW);
                        }
                    }

                    SetCursorPos(screenBounds.Width + 5000, 0);
                    SendMessage(boxeeLaunchProcess.MainWindowHandle, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
                    if (backgroundForm != null)
                    {
                        SetWindowPos(backgroundForm.Handle, HWND_TOP, 0, 0, screenBounds.Width, screenBounds.Height, SWP_SHOWWINDOW);
                    }

                    SetForegroundWindow(boxeeLaunchProcess.MainWindowHandle);

                    // Start the watch timer to see if media center comes back into the foreground
                    _windowWatchTimer.Change(0, 500);

                    boxeeLaunchProcess.WaitForExit();

                    if (_mediaCenterProcess != null)
                    {
                        _windowWatchTimer.Change(Timeout.Infinite, Timeout.Infinite);

                        // Bring MC to the front maximized
                        SendMessage(_mediaCenterProcess.MainWindowHandle, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
                        if (backgroundForm != null)
                        {
                            SetWindowPos(backgroundForm.Handle, HWND_TOP, 0, 0, screenBounds.Width, screenBounds.Height, SWP_SHOWWINDOW);
                        }

                        SetForegroundWindow(_mediaCenterProcess.MainWindowHandle);

                        ShowCursor(false);
                        SetCursorPos(screenBounds.Width + 5000, 0);

                        // When we come back into media center, the mouse and control bar will be shown.
                        // Send the alt key, as it will imediately force the mouse and control bar to be hidden.
                        Thread.Sleep(500);
                        keybd_event(VK_ALT, 0, 0, 0);
                        Thread.Sleep(500);
                        keybd_event(VK_ALT, 0, KEYEVENT_KEYUP, 0);
                    }
                }
                finally
                {
                    ShowTaskbar();

                    if (backgroundForm != null)
                    {
                        backgroundForm.Close();

                        if (!backgroundForm.IsDisposed)
                        {
                            backgroundForm.Dispose();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogException(ex);
                throw;
            }
        }

        private static void LogException(Exception ex)
        {
            if (!string.IsNullOrEmpty(_logPath))
            {
                using (StreamWriter writer = new StreamWriter(_logPath, true))
                {
                    writer.WriteLine(DateTime.Now.ToString());
                    writer.WriteLine(ex.Message);
                    writer.WriteLine(ex.Source);
                    writer.WriteLine(ex.TargetSite);
                    writer.WriteLine(ex.StackTrace);

                    StackTrace trace = new StackTrace(ex);
                    if (trace.FrameCount > 0)
                    {
                        StackFrame frame = trace.GetFrame(0);
                        if (frame != null)
                        {
                            MethodBase method = frame.GetMethod();
                            writer.WriteLine(string.Format("{0}, Line {1}", method.Name, frame.GetFileLineNumber()));
                        }
                    }

                    writer.WriteLine();
                }

                if (ex.InnerException != null)
                {
                    LogException(ex.InnerException);
                }
            }
        }

        private static void LogMessage(string message)
        {
            if (!string.IsNullOrEmpty(_logPath))
            {
                using (StreamWriter writer = new StreamWriter(_logPath, true))
                {
                    writer.WriteLine(DateTime.Now.ToString());
                    writer.WriteLine(message);
                    writer.WriteLine();
                }
            }
        }

        private static void ShowTaskbar()
        {
            ShowWindow(FindWindow("Shell_TrayWnd", ""), SW_SHOW);
        }

        private static void HideTaskbar()
        {
            ShowWindow(FindWindow("Shell_TrayWnd", ""), SW_HIDE);
        }

        [DllImport("User32.dll")]
        private static extern void SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int width, int height, uint flags);

        [DllImport("User32.dll")]
        private static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);

        [DllImport("User32.dll")]
        private static extern int SetForegroundWindow(IntPtr hWnd);

        [DllImport("User32.dll")]
        private static extern IntPtr GetForegroundWindow();

        [DllImport("User32.dll")]
        private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);

        [DllImport("User32.dll")]
        private static extern long SetCursorPos(int x, int y);

        [DllImport("User32.dll")]
        private static extern int ShowCursor(bool bShow);

        [DllImport("User32.dll")]
        private static extern int FindWindow(string className, string windowText);

        [DllImport("User32.dll")]
        private static extern int ShowWindow(int hwnd, int command);
    }
}
